// Created by Windward Studios - no copyright is claimed. This code can be used in
// any manner by anyone for any reason. There is no copyright of any kind on it. You may
// use it in commercial products. You may change it without sharing those changes.
// We ask that you keep the "created by Windward Studios" in a comment at the top.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Reflection;
using Kailua.net.windward.utils;

namespace net.windward.utils.ado.DB2
{
	/// <summary>
	/// Represents a DB2 server. DB2 has no way to get a list of databases on a server. What it does is return alll
	/// server/instance/database triples it can find. So for a server that the enum does not find, there is no way
	/// to get an enum of databases.
	/// </summary>
	public class WrDB2Server : IWrServer
	{
		private readonly DbProviderFactory provider;
		private readonly string name;
		private string[] databases;
		private readonly WrDB2Vendor vendor;

		/// <summary>
		/// Create a DB2 server object.
		/// </summary>
		/// <param name="provider">The provider for this server.</param>
		/// <param name="name">The name of the server.</param>
		/// <param name="databases">Databases on this server.</param>
		public WrDB2Server(DbProviderFactory provider, string name, string[] databases)
		{
			this.provider = provider;
			this.name = name;
			this.databases = databases;
			vendor = new WrDB2Vendor();
		}

		#region IWrServer Members

		/// <summary>
		/// The name of the server.
		/// </summary>
		public string Name
		{
			get { return name; }
		}

		/// <summary>
		/// All databases on this server.
		/// </summary>
		/// <param name="credentials">The user credentials to access the database.</param>
		/// <returns>All databases on this server.</returns>
		public IWrDatabase[] GetDatabases(WrCredentials credentials)
		{

			// from http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=/com.ibm.swg.im.dbclient.adonet.ref.doc/doc/DB2DataSourceEnumeratorClassGetDataSourcesMethods.html
			try
			{
				// return new DB2DataSourceEnumerator.GetDataSources(String Host, String Port, String UID, String PWD);
				Object[] args = new Object[4];
				if (name.IndexOf(':') == -1)
					args[0] = name;
				else
				{
					int pos = name.IndexOf(':');
					int port;
					if (int.TryParse(name.Substring(pos + 1), out port))
					{
						Trap.trap();
						args[0] = name.Substring(0, pos);
						args[1] = Convert.ToString(port);
					}
					else
						args[0] = name;
				}
				args[2] = credentials.FullName;
				args[3] = credentials.Password;

				Assembly db2Assembly = Assembly.GetAssembly(provider.GetType());
				Type db2EnumType = db2Assembly.GetType("IBM.Data.DB2.DB2DataSourceEnumerator");
				object inst = db2EnumType.GetField("Instance").GetValue(null);
				DataTable table = (DataTable)inst.GetType().InvokeMember("GetDataSources", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, inst, args);
				WrDB2Server [] servers = WrDB2Vendor.ReadServers(provider, table);

				List<string> dbNames = new List<string>();
				foreach (WrDB2Server srvrOn in servers)
					if (srvrOn.Name.ToUpper() == name.ToUpper())
						foreach (string dbOn in srvrOn.databases)
							dbNames.Add(dbOn);

				List <WrDB2Database> rtn = new List<WrDB2Database>();
				foreach (string dbName in dbNames)
					rtn.Add(new WrDB2Database(provider, name, dbName, credentials));
				return rtn.ToArray();

			} catch (TargetInvocationException ex)
			{
				throw ex.InnerException;
			}
			catch (Exception)
			{
				Trap.trap();
			}

			// bugbug - major functionality issue!
			// we have to get it as part of the server enum.
			return OldApproach(credentials);
		}

		private IWrDatabase[] OldApproach(WrCredentials credentials)
		{
			if (databases == null)
			{
				databases = new string[0];
				IWrServer[] servers = vendor._GetServers(false);
				foreach (IWrServer srvrOn in servers)
					if (srvrOn.Name.ToUpper() == name.ToUpper())
					{
						databases = ((WrDB2Server) srvrOn).databases;
						break;
					}
			}

			ArrayList rtn = new ArrayList();
			foreach (string dbName in databases)
				rtn.Add(new WrDB2Database(provider, name, dbName, credentials));
			return (WrDB2Database[])rtn.ToArray(typeof(WrDB2Database));
		}

		/// <summary>
		/// All databases on this server. Returns [0] as we don't know how to pull this in DB2.
		/// </summary>
		/// <param name="connStr">The connection string to access the database.</param>
		/// <returns>All databases on this server.</returns>
		public IWrDatabase[] GetDatabases(string connStr)
		{
			Trap.trap();
			WrConnectionParams connParams = vendor.ConnectionParams(connStr);
			return GetDatabases(connParams.Credentials);
		}

		#endregion

		/// <summary>
		/// Create a database object.
		/// </summary>
		/// <param name="database">The name of the database.</param>
		public IWrDatabase CreateDatabase(string database)
		{
			return new WrDB2Database(provider, name, database, new WrCredentials());
		}

		/// <summary>
		/// Compares the current instance with another object of the same type.
		/// </summary>
		/// <param name="obj">An object to compare with this instance. </param>
		/// <returns>a 32-bit signed integer that indicates the relative order of the objects being compared.</returns>
		public int CompareTo(object obj)
		{
			WrDB2Server db = obj as WrDB2Server;
			if (db == null)
				return 1;
			return name.CompareTo(db.name);
		}

	}
}
